
[SECTION .bss]
StackSpace      resb    2 * 1024
StackTop:       ; 栈顶

[section .text]
global _start
global restart

global divide_error
global single_step_exception
global nmi
global breakpoint_exception
global overflow
global bounds_check
global inval_opcode
global copr_not_available
global double_fault
global copr_seg_overrun
global inval_tss
global segment_not_present
global stack_exception
global general_protection
global page_fault
global copr_error

global hwint00
global hwint01
global hwint02
global hwint03
global hwint04
global hwint05
global hwint06
global hwint07
global hwint08
global hwint09
global hwint0a
global hwint0b
global hwint0c
global hwint0d
global hwint0e
global hwint0f
global sys_call

extern kernel_main
extern cstart
extern exception_handler
extern hw_int_proc
extern g_cur_pos
extern g_text_attr
extern g_gdt_ptr
extern g_idt_ptr
extern g_proc_ready
extern g_tss
extern clock_handler
extern g_reenter
extern g_irq_table
extern g_syscall_tab

extern print_char

[SECTION .bss]
;ALIGN 32
;[BITS 32]
KERNEL_STACK: resb 2 * 1024
KERNEL_STACK_TOP:; equ $ - 4

[section .text]

_start:
    mov esp, KERNEL_STACK_TOP
    and esp, 0xFFFFFFFC ;align 4

    sgdt [g_gdt_ptr]
    call cstart

    lgdt [g_gdt_ptr]
    lidt [g_idt_ptr]

    jmp 8:csinit ;LABEL_DESC_FLAT_C

    csinit:
    ;ud2
    ;jmp 0x40:0
    ;mov eax, 0x1234
    ;mov ebx, 0
    ;div ebx
	push dword 'R'
    call print_char
    add esp, 4
    jmp $


    ;push    0
    ;popfd    ; Pop top of stack into EFLAGS
;    sti

    xor eax, eax
    mov ax, 0x20; SELECTOR_TSS = INDEX_TSS*8
    ltr ax

    jmp kernel_main

; 中断和异常 -- 异常
divide_error:
    push    0xFFFFFFFF    ; no err code
    push    0        ; vector_no    = 0
    jmp    exception
single_step_exception:
    push    0xFFFFFFFF    ; no err code
    push    1        ; vector_no    = 1
    jmp    exception
nmi:
    push    0xFFFFFFFF    ; no err code
    push    2        ; vector_no    = 2
    jmp    exception
breakpoint_exception:
    push    0xFFFFFFFF    ; no err code
    push    3        ; vector_no    = 3
    jmp    exception
overflow:
    push    0xFFFFFFFF    ; no err code
    push    4        ; vector_no    = 4
    jmp    exception
bounds_check:
    push    0xFFFFFFFF    ; no err code
    push    5        ; vector_no    = 5
    jmp    exception
inval_opcode:
    push    0xFFFFFFFF    ; no err code
    push    6        ; vector_no    = 6
    jmp    exception
copr_not_available:
    push    0xFFFFFFFF    ; no err code
    push    7        ; vector_no    = 7
    jmp    exception
double_fault:
    push    8        ; vector_no    = 8
    jmp    exception
copr_seg_overrun:
    push    0xFFFFFFFF    ; no err code
    push    9        ; vector_no    = 9
    jmp    exception
inval_tss:
    push    10        ; vector_no    = A
    jmp    exception
segment_not_present:
    push    11        ; vector_no    = B
    jmp    exception
stack_exception:
    push    12        ; vector_no    = C
    jmp    exception
general_protection:
    push    13        ; vector_no    = D
    jmp    exception
page_fault:
    push    14        ; vector_no    = E
    jmp    exception
copr_error:
    push    0xFFFFFFFF    ; no err code
    push    16        ; vector_no    = 10h
    jmp    exception

exception:
    call    exception_handler
    add    esp, 4*2    ; 让栈顶指向 EIP，堆栈中从顶向下依次是：EIP、CS、EFLAGS
    hlt

INT_M_CTL equ 0x20 ; I/O port for interrupt controller        <Master>
EOI equ 0x20 ;0010 0000 (D7-D0) D3=D4=0
INT_M_CTLMASK equ 0x21; setting bits in this port disables ints   <Master>
INT_S_CTL   equ 0xA0    ; I/O port for second interrupt controller  <Slave>
INT_S_CTLMASK   equ 0xA1    ; setting bits in this port disables ints   <Slave>

%macro hwint_master 1
    call save

    in al, INT_M_CTLMASK
    or al, (1 << %1) ;不允许再发生当前中断
    out INT_M_CTLMASK, al ;0x21 A0=奇? OCW1?

    ;inc byte [gs:0]
    mov al, EOI
    out INT_M_CTL, al ;0x20 A0=偶地址 D3=D4=0:OCW2

    sti ; CPU在响应中断的过程中会自动关中断,这句之后就允许响应新的中断

    push %1
    call [g_irq_table + 4 * %1]
    pop ecx
    cli

    in al, INT_M_CTLMASK ;0x21
    and al, ~(1 << %1) ;允许再发生当前中断
    out INT_M_CTLMASK, al
    ret
%endmacro

%macro hwint_slave 1
    call save

    in al, INT_S_CTLMASK
    or al, (1 << (%1 - 8)) ;不允许再发生当前中断
    out INT_S_CTLMASK, al ;0x21 A0=奇? OCW1?

    mov al, EOI
    out INT_M_CTL, al ;0x20 A0=偶地址 D3=D4=0:OCW2
    nop               ;置EOI位(slave)
    out INT_S_CTL, al ;一定注意：slave和master都要置EOI

    sti ; CPU在响应中断的过程中会自动关中断,这句之后就允许响应新的中断

    push %1
    call [g_irq_table + 4 * %1]
    pop ecx
    cli

    in al, INT_S_CTLMASK ;0x21
    and al, ~(1 << (%1 - 8)) ;允许再发生当前中断
    out INT_S_CTLMASK, al
    ret
%endmacro

ALIGN 16
save:
    pushad ;push all common regs:eax, ecx, edx, ebx, kernel_esp, ebp, esi, edi  to tss.esp0-4*6
    push ds
    push es
    push fs
    push gs

    mov di, ss
    mov ds, di ;set to ring0
    mov es, di ;set to ring0

    mov esi, esp ;esi->&curProcTable.regs.gs
    inc dword [g_reenter] ;运行到这前（用户程序运行时）g_reenter值应该为-1
    cmp dword [g_reenter], 0
    jne hi00_reenter

    mov esp, StackTop ;切至内核栈 为何非重入时切到内核栈，重入时不切呢？事实上重入时已经在工作在内核栈了，参考zj[关于中断重入时压栈位置的疑问]章节
    push restart
    jmp [esi + RETADR]

    hi00_reenter:
        push restart_reenter ;中断重入时，仍然调用clock_handler
        jmp [esi + RETADR]


P_STACKBASE    equ    0
GSREG        equ    P_STACKBASE ;0
FSREG        equ    GSREG        + 4;4
ESREG        equ    FSREG        + 4;8
DSREG        equ    ESREG        + 4;12
EDIREG        equ    DSREG        + 4;16
ESIREG        equ    EDIREG        + 4;20
EBPREG        equ    ESIREG        + 4;24
KERNELESPREG    equ    EBPREG        + 4;28
EBXREG        equ    KERNELESPREG    + 4;32
EDXREG        equ    EBXREG        + 4;36
ECXREG        equ    EDXREG        + 4;40
EAXREG        equ    ECXREG        + 4;44
RETADR        equ    EAXREG        + 4;48
EIPREG        equ    RETADR        + 4;52
CSREG        equ    EIPREG        + 4;56
EFLAGSREG    equ    CSREG        + 4;60
ESPREG        equ    EFLAGSREG    + 4;64
SSREG        equ    ESPREG        + 4 ;68
P_STACKTOP    equ    SSREG        + 4 ;72
P_LDT_SEL    equ    P_STACKTOP ;72
P_LDT        equ    P_LDT_SEL    + 4 ;76
TSS3_S_SP0    equ    4

restart:
    mov esp, [g_proc_ready] ;esp -> &proc_table[0].regs.gs
    lldt [esp + P_LDT_SEL] ;+72
    lea eax, [esp + P_STACKTOP] ;eax=esp+72  -> &proc_table[0].regs.ss + 4
    mov dword [g_tss + TSS3_S_SP0], eax ;tss.esp0 -> &proc_table[0].regs.ss + 4  中断发生时，将现场压栈到何处

    restart_reenter:
        dec dword [g_reenter]

        pop gs
        pop fs
        pop es
        pop ds
        popad
        add esp, 4
        iretd;依次弹出eip cs eflags esp ss

ALIGN 16
hwint00:
    hwint_master 0
ALIGN 16
hwint01:
    hwint_master 1
ALIGN 16
hwint02:
    hwint_master 2
ALIGN 16
hwint03:
    hwint_master 3
ALIGN 16
hwint04:
    hwint_master 4
ALIGN 16
hwint05:
    hwint_master 5
ALIGN 16
hwint06:
    hwint_master 6
ALIGN 16
hwint07:
    hwint_master 7

ALIGN 16
hwint08:
    hwint_slave 8

ALIGN 16
hwint09:
    hwint_slave 9

ALIGN 16
hwint0a:
    hwint_slave 10

ALIGN 16
hwint0b:
    hwint_slave 11

ALIGN 16
hwint0c:
    hwint_slave 12

ALIGN 16
hwint0d:
    hwint_slave 13

ALIGN 16
hwint0e:
    hwint_slave 14

ALIGN 16
hwint0f:
    hwint_slave 15

sys_call:
    call save

    push dword [g_proc_ready]
    sti
    push edx
    push ecx
    push ebx
    call [g_syscall_tab + 4 * eax]
    add esp, 4 * 4

    mov [esi + EAXREG], eax
    cli
    ret
